import { getCtrl, getView, IPSApplication, IPSAppView, IPSDEToolbar } from '@ibizlab/model';
import { Locator, Page, test } from '@playwright/test';
import { notNilEmpty } from 'qx-util';
import { ViewContext } from '../../context';
import { login } from '../../utils';
import { IBizToolbar } from '../../widget';
import { IEnvironment } from '../../environment';
import { ModelService } from '../../service';

/**
 * 页面
 *
 * @author chitanda
 * @date 2022-04-22 09:04:32
 * @export
 * @abstract
 * @class IBizView
 */
export abstract class IBizView {
  /**
   * 当前视图界面元素域
   *
   * @author chitanda
   * @date 2022-04-21 20:04:23
   * @type {Locator}
   */
  l!: Locator;

  /**
   * 浏览器上下文
   *
   * @author chitanda
   * @date 2022-04-21 19:04:30
   * @protected
   * @type {ViewContext}
   */
  protected _ctx?: ViewContext;
  get ctx(): ViewContext {
    if (!this._ctx) {
      this._ctx = new ViewContext(this.page.context());
    }
    return this._ctx;
  }

  /**
   * 应用模型
   *
   * @author chitanda
   * @date 2022-04-21 16:04:46
   * @type {IPSApplication}
   */
  appModel: IPSApplication;

  /**
   * 当前视图模型
   *
   * @author chitanda
   * @date 2022-04-21 16:04:51
   * @type {IPSAppView}
   */
  viewModel: IPSAppView;

  /**
   * 工具栏模型
   *
   * @author chitanda
   * @date 2022-04-21 16:04:18
   * @readonly
   * @type {IBizToolbar}
   */
  protected _toolbar?: IBizToolbar;
  get toolbar(): IBizToolbar {
    if (!this._toolbar) {
      const toolbar = this.l.locator(`.toolbar-container.view-toolbar`);
      const toolbarModel = getCtrl(this.viewModel, 'toolbar') as IPSDEToolbar;
      this._toolbar = new IBizToolbar(toolbar, toolbarModel);
    }
    return this._toolbar;
  }

  /**
   * Creates an instance of IBizView.
   *
   * @author chitanda
   * @date 2022-04-22 12:04:39
   * @param {IEnvironment} env 环境配置
   * @param {ModelService} modelService 模型服务
   * @param {Page} page 页面
   * @param {string} viewModelPath 视图模型路径
   */
  constructor(public env: IEnvironment, public modelService: ModelService, public page: Page, public viewModelPath: string) {
    this.appModel = modelService.app;
    this.viewModel = getView(modelService.app, viewModelPath) as IPSAppView;
  }

  /**
   * 打开界面并登录
   *
   * @author chitanda
   * @date 2022-04-21 20:04:35
   * @param {string} url 跳转的界面
   * @param {string} [username=environment.user.username]
   * @param {string} [password=environment.user.password]
   * @return {*}  {Promise<void>}
   */
  async goto(url: string, username: string = this.env.user.username, password: string = this.env.user.password): Promise<void> {
    await test.step('打开界面并登录', async () => {
      if (notNilEmpty(this.env.user.token)) {
        this.ctx.addToken(this.env.user.token!);
      }
      await this.page.goto(url);
      await login(this.page, username, password);
      await this.page.waitForSelector('.app-loading-x', { state: 'visible' });
    });
  }

  /**
   * 初始化
   *
   * @author chitanda
   * @date 2022-04-21 20:04:40
   * @return {*}  {Promise<void>}
   */
  async init(): Promise<void> {
    await test.step('视图初始化', async () => {
      await this.onInit();
    });
  }

  /**
   * 视图初始化
   *
   * @author chitanda
   * @date 2022-04-22 09:04:54
   * @protected
   * @abstract
   * @return {*}  {Promise<void>}
   */
  protected abstract onInit(): Promise<void>;

  /**
   * 获取指定选择器中满足条件的第一个
   *
   * @author chitanda
   * @date 2022-04-17 11:04:38
   * @param {string} selector
   * @return {*}  {Locator}
   */
  async el(selector: string): Promise<Locator> {
    const locator = this.l ? this.l.locator(selector) : this.page.locator(selector);
    const count = await locator.count();
    if (count > 0) {
      if (count === 1) {
        return locator;
      }
      return locator.first();
    }
    throw new Error(`找不到元素：${selector}`);
  }

  /**
   * 点击指定元素
   *
   * @author chitanda
   * @date 2022-04-20 10:04:31
   * @param {string} selector
   * @return {*}  {Promise<void>}
   */
  click(selector: string): Promise<void> {
    return this.page.click(selector);
  }

  /**
   * 等待页面某元素加载完成，默认超时时间为5秒
   *
   * @author chitanda
   * @date 2022-04-17 11:04:35
   * @param {string} selector
   * @return {*}
   */
  waitEl(selector: string) {
    return this.page.waitForSelector(selector, { state: 'attached' });
  }

  /**
   * 等待
   *
   * @author chitanda
   * @date 2022-04-18 10:04:08
   * @param {number} timeout 等待时间，单位：秒
   * @return {*}  {Promise<void>}
   */
  sleep(timeout: number): Promise<void> {
    return this.page.waitForTimeout(timeout * 1000);
  }
}
